package net.gdface.facedb;

import static com.gitee.l0km.com4j.base.BinaryUtils.*;
import static com.google.common.base.Preconditions.*;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.gitee.l0km.com4j.base.Judge;
import com.gitee.l0km.ximage.BaseLazyImage;
import com.gitee.l0km.ximage.ImageErrorException;
import com.gitee.l0km.ximage.LazyImageFactory;
import com.gitee.l0km.ximage.NotImageException;
import com.gitee.l0km.ximage.UnsupportedFormatException;
import com.google.common.base.MoreObjects;
import com.google.common.base.Predicates;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;

import gu.sql2java.exception.RuntimeDaoException;
import gu.sql2java.store.DataNotFoundException;
import net.gdface.facedb.db.FaceBean;
import net.gdface.facedb.db.FeatureBean;
import net.gdface.facedb.db.ImageBean;
import net.gdface.facedb.db.StoreBean;
import net.gdface.sdk.CodeInfo;
/**
 * 数据库操作扩展
 * @author guyadong
 *
 */
class Dao extends BaseDao {
	final FacedbTypeTransformer trans = new FacedbTypeTransformer();

	/**
	 * 创建{@link ImageBean}对象,填充图像基本信息,同时创建对应的{@link StoreBean}对象
	 * @param imageBytes
	 * @return 返回 {@link ImageBean}和{@link StoreBean}对象
	 * @throws NotImageException
	 * @throws UnsupportedFormatException
	 */
	protected static ImageBean makeImageBean(ByteBuffer imageBytes) throws NotImageException, UnsupportedFormatException{
		if(Judge.isEmpty(imageBytes)){
			return null;
		}
		LazyImageFactory factory = BaseLazyImage.getLazyImageFactory();
		BaseLazyImage image = factory.create(imageBytes);
		String md5 = getMD5String(imageBytes);
		ImageBean imageBean = new ImageBean();
		imageBean.setMd5(md5);
		imageBean.setWidth(image.getWidth());
		imageBean.setHeight(image.getHeight());
		imageBean.setFormat(image.getSuffix());
		return imageBean;
	}
	protected static FeatureBean makeFeatureBean(ByteBuffer feature){
		return FacedbTypeTransformer.FEATUREBEAN_FUN.apply(feature);
	}
	/**
	 * 删除IMAGE表同时删除STORE表记录
	 */
	@Override
	protected int daoDeleteImage(String md5) throws RuntimeDaoException {
		int c = super.daoDeleteImage(md5);
		try {
			getImageURLStore().delete(md5);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		return c;
	}
	/**
	 * @param md5
	 * @param cascade 是否级联删除对应的特征
	 * @return
	 */
	protected int daoDeleteImage(String md5, boolean cascade) {
		List<FeatureBean> featureBeans = this.daoGetFeaturesByImageMd5(md5);
		int result = daoDeleteImage(md5);
		if(result==1 && cascade){
			this.daoDeleteFeatures(featureBeans);
		}
		return result;
	}
	/**
	 * @param imgMd5List
	 * @param cascade 是否级联删除对应的特征
	 * @return 删除图像记录数量
	 */
	protected int daoDeleteImages(Collection<String> imgMd5List, boolean cascade){
		int count = 0;
		imgMd5List = MoreObjects.firstNonNull(imgMd5List, Collections.<String>emptyList());
		for(String md5:imgMd5List){
			count += daoDeleteImage(md5, cascade);
		}
		return count;
	}
	protected int daoDeleteFeature(String featureId, boolean cascade) {
		List<ImageBean> imageBeans = this.daoGetImagesByFeatureId(featureId);
		int result = daoDeleteFeature(featureId);
		if(result == 1){
			if(cascade){
				this.daoDeleteImages(imageBeans);
			}
		}
		return result;
	}
	protected int daoDeleteFeatures(Collection<String> featureIdList, boolean cascade){
		int count = 0;
		featureIdList = MoreObjects.firstNonNull(featureIdList, Collections.<String>emptyList());
		for(String featureId:featureIdList){
			count += daoDeleteFeature(featureId, cascade);
		}
		return count;
	}
	protected List<ImageBean> daoGetImagesByFeatureId(String featureId) {
		List<FaceBean> faceBeans = daoGetFaceBeansByFeatureMd5OnFeature(featureId);
		List<ImageBean> imageBeans = Lists.transform(faceBeans, daoCastFaceToReferencedImageBean);
		// 去除重复元素和null
		HashSet<ImageBean> imageSet = Sets.newHashSet(Iterables.filter(imageBeans, Predicates.notNull()));
		return Lists.newArrayList(imageSet);
	}
	/**
	 * 根据提供的主键ID,返回图像数据记录
	 * @param primaryKey 主键
	 * @param refType 主键引用类型
	 * @return 图像数据记录
	 */
	protected ImageBean daoGetImage(String primaryKey,RefSrcType refType) {
		checkArgument(refType!=null,"refType is null");
		if(null == primaryKey){
			return null;
		}
		
		switch (MoreObjects.firstNonNull(refType,RefSrcType.DEFAULT)) {
		case FEATURE:
		{
			List<ImageBean> beans = daoGetImagesByFeatureId(primaryKey);
			return beans.size() == 1 ? beans.get(0) : null;
		}
		case FACE:
		{
			FaceBean bean = daoGetFace(Integer.valueOf(primaryKey));
			return null == bean ? null : daoGetImage(bean.getImageMd5());
		}
		case IMAGE:
		case DEFAULT:
			return daoGetImage(primaryKey);
		default:
			return null;
		}		
	}
	protected byte[] daoGetImageBytes(String imageMd5) {
		ImageBean image = daoGetImage(imageMd5);
		if(image != null && image.getLocation() != null){
			try {
				return getBytes(new URL(image.getLocation()));
			} catch (DataNotFoundException e) {
				// see also 
				// net.gdface.url.LocalBinaryStore.FileConnection.getInputStream() 
				// gu.sql2java.BaseColumnStore.DatabaseURLConnection.getInputStream()
				return null;
			}catch ( IOException e) {
				Throwables.throwIfUnchecked(e);
				throw new RuntimeException(e);
			}
		}else{
			return null;
		}
	}
	protected List<FeatureBean> daoGetFeaturesByImageMd5(String imageMd5) {
		List<FaceBean> faceBeans = daoGetFaceBeansByImageMd5OnImage(imageMd5);
		List<FeatureBean> featureBeans = Lists.transform(faceBeans, daoCastFaceToReferencedFeatureBean);
		// 去除重复元素和null
		HashSet<FeatureBean> featureSet = Sets.newHashSet(Iterables.filter(featureBeans, Predicates.notNull()));
		return Lists.newArrayList(featureSet);
	}
	protected ImageBean daoAddImage(ByteBuffer imageBytes, Collection<FaceBean> impFaceByImageMd5) throws DuplicateRecordException{
		if(Judge.isEmpty(imageBytes)){
			return null;
		}
		ImageBean image;
		try {
			image = makeImageBean(imageBytes);
		} catch (ImageErrorException e) {
			throw new RuntimeException(e);
		}
		try {
			URL storedUrl = getImageURLStore().store(imageBytes, image.getMd5(), image.getFormat(), false, false);
			image.setLocation(storedUrl.toString());
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		if(impFaceByImageMd5 != null){
			image.setFaceNum(impFaceByImageMd5.size());
		}
		return daoAddImage(image,  impFaceByImageMd5);
	}
	protected ImageBean daoAddImage(ByteBuffer imgData, List<CodeInfo> features) throws DuplicateRecordException {
		checkArgument(null !=imgData ,"imgData is null");
		checkArgument(null != features && !features.isEmpty(),"features is nul or empty" );
		checkArgument(!Iterables.tryFind(features, Predicates.isNull()).isPresent(),"exist null element in features");
		// 这里不能用transform对象，因为transform对象迭代时不可修改
		List<FaceBean> faceBeans = trans.new Fun<List<CodeInfo>,List<FaceBean>>(){}.apply(features);
		List<FeatureBean> featureBeans =  trans.new Fun<List<CodeInfo>,List<FeatureBean>>(){}.apply(features);
		checkState(!Iterables.tryFind(featureBeans, Predicates.isNull()).isPresent(),"exist null code field in features");
		daoAddFeatures(featureBeans);
		return daoAddImage(imgData, faceBeans);
	}
	protected FeatureBean daoAddFeature(ByteBuffer feature, Map<ByteBuffer, CodeInfo> faces) throws DuplicateRecordException {
		checkArgument(null != faces && !faces.isEmpty(),"faces is null or empty");
		checkArgument(null != feature,"feature is null");
		// 这里不能用transform对象，因为transform对象迭代时不可修改
		Map<ByteBuffer, FaceBean> faceBeanMap = trans.new Fun<Map<ByteBuffer, CodeInfo>,Map<ByteBuffer, FaceBean>>(){}.apply(faces);
		FeatureBean featureBean = makeFeatureBean(feature);
		featureBean = this.daoAddFeature(featureBean,faceBeanMap.values());
		for(Entry<ByteBuffer, FaceBean> entry:faceBeanMap.entrySet()){
			daoAddImage(entry.getKey(),Arrays.asList(entry.getValue()));
		}
		return featureBean;
	}
}
